﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Область СлужебныйПрограммныйИнтерфейс

// Проверяет, является ли переданная строка внутренней навигационной ссылкой.
//  
// Параметры:
//  Строка - Строка - навигационная ссылка.
//
// Возвращаемое значение:
//  Булево - результат проверки.
//
Функция ЭтоНавигационнаяСсылка(Строка) Экспорт
	
	Возврат СтрНачинаетсяС(Строка, "e1c:")
		Или СтрНачинаетсяС(Строка, "e1cib/")
		Или СтрНачинаетсяС(Строка, "e1ccs/");
	
КонецФункции

// Конвертирует параметры запуска текущего сеанса в передаваемые параметры в скрипт
// Например, на вход программа может быть запущена с ключом:
// /C "ПараметрыЗапускаИзВнешнейОперации=/TestClient -TPort 48050 /C РежимОтладки;РежимОтладки"
// то в скрипт будет передана строка: "/TestClient -TPort 48050 /C РежимОтладки".
//
// Возвращаемое значение:
//  Строка - значение параметра.
//
Функция ПараметрыЗапускаПредприятияИзСкрипта() Экспорт
	
	Перем ЗначениеПараметра;
	
	ПараметрыЗапуска = СтроковыеФункцииКлиентСервер.ПараметрыИзСтроки(ПараметрЗапуска);
	Если Не ПараметрыЗапуска.Свойство("ПараметрыЗапускаИзВнешнейОперации", ЗначениеПараметра) Тогда 
		ЗначениеПараметра = "";
	КонецЕсли;
	
	Возврат ЗначениеПараметра;
	
КонецФункции

#Область ВнешниеКомпоненты

// Контекст подключения компоненты.
// 
// Возвращаемое значение:
//  Структура - контекст подключения компоненты:
//   * Оповещение - Неопределено, ОписаниеОповещения - оповещение.
//   * Идентификатор - Строка - идентификатор объекта компоненты, идентификатор внешней компоненты.
//   * Версия - Неопределено, Строка - версия компоненты.
//   * Местоположение - Строка - местоположение макета или ссылка на внешнюю компоненту.
//   * ИсходноеМестоположение - Строка - местоположение начального вызова, для хранения кэша.
//   * Кэшировать - Булево -  (по умолчанию Истина) признак сохранения подключенной компоненты в кэше;
//                 Ложь - в память могут быть загружены одновременно несколько объектов одной компоненты,
//                 что может вызвать ошибки.
//   * ПредложитьУстановить - Булево - предлагать устанавливать компоненту.
//   * БылаПопыткаУстановки - Булево - в процессе подключения была попытка установки.
//   * ПредложитьЗагрузить - Булево - предлагать загружать компоненту с сайта ИТС.
//   * ТекстПояснения - Строка - для чего нужна компонента и что не будет работать, если ее не устанавливать. 
//   * ИдентификаторыСозданияОбъектов - Массив -идентификатор создания экземпляра модуля объекта,
//                 используется только для компонент, у которых есть несколько идентификаторов создания объектов,
//                 при задании параметр Идентификатор будет игнорироваться.
//   * ВыполненПоискНовойВерсии - Булево - поиск новой версии компоненты производился.
//   * Изолированно - Булево, Неопределено - (по умолчанию Неопределено) если Истина, компонента будет подключена
//                изолированно, в этом случае внешняя компонента загружается в отдельный процесс операционной системы;
//                Ложь - в этом случае внешняя компонента будет выполняться в том же процессе операционной системы,
//                который выполняет код встроенного языка; Неопределено - поддерживается поведение платформы по умолчанию:
//                изолированно - если компонентой поддерживается только этот режим, не изолированно - в остальных случаях.
//                См. https://its.1c.eu/db/v83doc#bookmark:dev:TI000001866
//    * ОбновлятьАвтоматически - Булево - (по умолчанию Истина) если ПредложитьЗагрузить = Истина, при загрузке 
//                внешней компоненты устанавливать признак ОбновлятьСПортала1СИТС
//
Функция КонтекстПодключенияКомпоненты() Экспорт
	
	Контекст = Новый Структура;
	Контекст.Вставить("Оповещение", Неопределено);
	Контекст.Вставить("Идентификатор", Неопределено);
	Контекст.Вставить("Версия", "");
	Контекст.Вставить("Местоположение", "");
	Контекст.Вставить("ИсходноеМестоположение", "");
	Контекст.Вставить("Кэшировать", Истина);
	Контекст.Вставить("ПредложитьУстановить", Истина);
	Контекст.Вставить("ПредложитьЗагрузить", Ложь);
	Контекст.Вставить("ОбновлятьАвтоматически", Истина);
	Контекст.Вставить("ТекстПояснения", "");
	Контекст.Вставить("ИдентификаторыСозданияОбъектов", Новый Массив);
	Контекст.Вставить("ВыполненПоискНовойВерсии", Ложь);
	Контекст.Вставить("Изолированно", Неопределено);
	Контекст.Вставить("БылаПопыткаУстановки", Ложь);
	
	Возврат Контекст;
	
КонецФункции

// Параметры:
//  Контекст - см. КонтекстПодключенияКомпоненты.
//
Асинх Функция ПодключитьКомпонентуАсинх(Контекст) Экспорт
	
	Если ПустаяСтрока(Контекст.Идентификатор) Тогда 
		КомпонентаСодержитЕдинственныйКлассОбъектов = (Контекст.ИдентификаторыСозданияОбъектов.Количество() = 0);
		
		Если КомпонентаСодержитЕдинственныйКлассОбъектов Тогда 
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось подключить внешнюю компоненту ""%1"" в клиентском приложении
				           |по причине:
				           |Недопустимо одновременно не указывать %2 и %3.'"), 
				Контекст.Местоположение, "Идентификатор", "ИдентификаторыСозданияОбъектов");
		Иначе
			// В случае, когда в компоненте есть несколько классов объектов
			// Идентификатор используется только для отображения компоненты в текстах ошибок.
			// Следует собрать идентификатор для отображения.
			Контекст.Идентификатор = СтрСоединить(Контекст.ИдентификаторыСозданияОбъектов, ", ");
		КонецЕсли;
	КонецЕсли;
	
	ПроверитьМестоположениеКомпоненты(Контекст.Идентификатор, Контекст.Местоположение); 
	
	Если Не Контекст.ВыполненПоискНовойВерсии Тогда
		
		Если Контекст.Кэшировать Тогда
			
			ПодключаемыйМодуль = ПолучитьОбъектКомпонентыИзКэша(Контекст.Местоположение);
			
			Если ПодключаемыйМодуль <> Неопределено Тогда
				Результат = РезультатПодключенияКомпоненты();
				Результат.Подключено = Истина;
				Результат.ПодключаемыйМодуль = ПодключаемыйМодуль;
				Возврат Результат;
			КонецЕсли;
			
			// Проверка факта подключения внешней компоненты в этом сеансе ранее.
			СимволическоеИмя = ПолучитьСимволическоеИмяКомпонентыИзКэша(Контекст.Местоположение);

			Если СимволическоеИмя <> Неопределено Тогда
			// Если в кэше уже есть символическое имя - значит к этому сеансу ранее компонента уже подключалась.
				Подключено = Истина;
				Контекст.Вставить("СимволическоеИмя", СимволическоеИмя);
				Возврат Ждать ПодключитьКомпонентуПослеПопыткиПодключенияАсинх(Подключено, Контекст);
			КонецЕсли;
			
		КонецЕсли;
	
		// Поиск более свежей версии компоненты в макетах и справочнике.
		Если ЭтоМакет(Контекст.Местоположение) Тогда
			
			РезультатПоискаВнешнейКомпоненты = Неопределено;
			Контекст.ИсходноеМестоположение = Контекст.Местоположение;
			
			Если ОбщегоНазначенияКлиент.ПодсистемаСуществует("СтандартныеПодсистемы.ВнешниеКомпоненты") Тогда
				МодульВнешниеКомпонентыСлужебныйКлиент = ОбщегоНазначенияКлиент.ОбщийМодуль(
					"ВнешниеКомпонентыСлужебныйКлиент");

				КонтекстПроверкиКомпоненты = КонтекстПодключенияКомпоненты();
				КонтекстПроверкиКомпоненты.Идентификатор = Контекст.Идентификатор;
				КонтекстПроверкиКомпоненты.Версия = Неопределено;
				КонтекстПроверкиКомпоненты.ПредложитьУстановить = Контекст.ПредложитьУстановить;
				КонтекстПроверкиКомпоненты.ПредложитьЗагрузить = Контекст.ПредложитьЗагрузить;
				КонтекстПроверкиКомпоненты.ОбновлятьАвтоматически = Контекст.ОбновлятьАвтоматически;
				КонтекстПроверкиКомпоненты.ТекстПояснения = Контекст.ТекстПояснения;
				КонтекстПроверкиКомпоненты.ИсходноеМестоположение = Контекст.ИсходноеМестоположение;
				КонтекстПроверкиКомпоненты.ИдентификаторыСозданияОбъектов = Контекст.ИдентификаторыСозданияОбъектов;

				РезультатПоискаВнешнейКомпоненты = Ждать МодульВнешниеКомпонентыСлужебныйКлиент.РезультатПроверкиДоступностиКомпоненты(
					КонтекстПроверкиКомпоненты);
			
			КонецЕсли;
			
			Контекст.ВыполненПоискНовойВерсии = Истина;
			Если РезультатПоискаВнешнейКомпоненты = Неопределено Или РезультатПоискаВнешнейКомпоненты.КомпонентаПоследнейВерсии = Неопределено Тогда
				КомпонентаПоследнейВерсии = СтандартныеПодсистемыВызовСервера.КомпонентаПоследнейВерсии(
					Контекст.Идентификатор, Контекст.ИсходноеМестоположение, Результат);
			Иначе
				КомпонентаПоследнейВерсии = РезультатПоискаВнешнейКомпоненты.КомпонентаПоследнейВерсии;
			КонецЕсли;

			Контекст.Местоположение = КомпонентаПоследнейВерсии.Местоположение;
			Контекст.Версия = КомпонентаПоследнейВерсии.Версия;
			
			Возврат Ждать ПодключитьКомпонентуАсинх(Контекст);
				
		КонецЕсли;
	КонецЕсли;
	
	// Генерация уникального имени.
	СимволическоеИмя = "С" + СтрЗаменить(Строка(Новый УникальныйИдентификатор), "-", "");
	Контекст.Вставить("СимволическоеИмя", СимволическоеИмя);
	
	Попытка
		
#Если МобильноеПриложениеКлиент ИЛИ МобильныйКлиент Тогда
		Подключено = Ждать ПодключитьВнешнююКомпонентуАсинх(Контекст.Местоположение, СимволическоеИмя);
#Иначе
		Подключено = Ждать ПодключитьВнешнююКомпонентуАсинх(Контекст.Местоположение, СимволическоеИмя,,
			ОбщегоНазначенияСлужебныйКлиентСервер.ТипПодключенияКомпоненты(Контекст.Изолированно));
#КонецЕсли
		
	Исключение
		
		ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось подключить внешнюю компоненту ""%1"" на клиенте
			           |%2
			           |по причине:
			           |%3'"),
			Контекст.Идентификатор,
			Контекст.Местоположение,
			ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
		
		Возврат ОшибкаПодключенияКомпоненты(ТекстОшибки);
		
	КонецПопытки;
	
	Возврат Ждать ПодключитьКомпонентуПослеПопыткиПодключенияАсинх(Подключено, Контекст);
	
КонецФункции

// Параметры:
//  Контекст - см. КонтекстПодключенияКомпоненты.
//
Процедура ПодключитьКомпоненту(Контекст) Экспорт
	
	Если ПустаяСтрока(Контекст.Идентификатор) Тогда 
		КомпонентаСодержитЕдинственныйКлассОбъектов = (Контекст.ИдентификаторыСозданияОбъектов.Количество() = 0);
		
		Если КомпонентаСодержитЕдинственныйКлассОбъектов Тогда 
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось подключить внешнюю компоненту ""%1"" в клиентском приложении
				           |по причине:
				           |Недопустимо одновременно не указывать %2 и %3.'"), 
				Контекст.Местоположение, "Идентификатор", "ИдентификаторыСозданияОбъектов");
		Иначе
			// В случае, когда в компоненте есть несколько классов объектов
			// Идентификатор используется только для отображения компоненты в текстах ошибок.
			// Следует собрать идентификатор для отображения.
			Контекст.Идентификатор = СтрСоединить(Контекст.ИдентификаторыСозданияОбъектов, ", ");
		КонецЕсли;
	КонецЕсли;
	
	ПроверитьМестоположениеКомпоненты(Контекст.Идентификатор, Контекст.Местоположение); 
	
	Если Не Контекст.ВыполненПоискНовойВерсии Тогда
		
		Если Контекст.Кэшировать Тогда
			
			ПодключаемыйМодуль = ПолучитьОбъектКомпонентыИзКэша(Контекст.Местоположение);
			
			Если ПодключаемыйМодуль <> Неопределено Тогда
				ПодключитьКомпонентуОповеститьОПодключении(ПодключаемыйМодуль, Контекст);
				Возврат;
			КонецЕсли;
			
			// Проверка факта подключения внешней компоненты в этом сеансе ранее.
			СимволическоеИмя = ПолучитьСимволическоеИмяКомпонентыИзКэша(Контекст.Местоположение);

			Если СимволическоеИмя <> Неопределено Тогда
			// Если в кэше уже есть символическое имя - значит к этому сеансу ранее компонента уже подключалась.
				Подключено = Истина;
				Контекст.Вставить("СимволическоеИмя", СимволическоеИмя);
				ПодключитьКомпонентуПослеПопыткиПодключения(Подключено, Контекст);
				Возврат;
			КонецЕсли;
		
		КонецЕсли;
		
		// Поиск более свежей версии компоненты в макетах и справочнике.
		Если ЭтоМакет(Контекст.Местоположение) Тогда
			Контекст.ИсходноеМестоположение = Контекст.Местоположение;
			Оповещение = Новый ОписаниеОповещения("ПодключитьПослеПоискаВнешнейКомпоненты", ЭтотОбъект, Контекст);
			Если ОбщегоНазначенияКлиент.ПодсистемаСуществует("СтандартныеПодсистемы.ВнешниеКомпоненты") Тогда
				МодульВнешниеКомпонентыСлужебныйКлиент = ОбщегоНазначенияКлиент.ОбщийМодуль(
					"ВнешниеКомпонентыСлужебныйКлиент");

				КонтекстПроверкиКомпоненты = КонтекстПодключенияКомпоненты();
				КонтекстПроверкиКомпоненты.Идентификатор = Контекст.Идентификатор;
				КонтекстПроверкиКомпоненты.Версия = Неопределено;
				КонтекстПроверкиКомпоненты.ПредложитьУстановить = Контекст.ПредложитьУстановить;
				КонтекстПроверкиКомпоненты.ПредложитьЗагрузить = Контекст.ПредложитьЗагрузить;
				КонтекстПроверкиКомпоненты.ОбновлятьАвтоматически = Контекст.ОбновлятьАвтоматически;
				КонтекстПроверкиКомпоненты.ТекстПояснения = Контекст.ТекстПояснения;
				КонтекстПроверкиКомпоненты.ИсходноеМестоположение = Контекст.ИсходноеМестоположение;
				КонтекстПроверкиКомпоненты.ИдентификаторыСозданияОбъектов = Контекст.ИдентификаторыСозданияОбъектов;

				МодульВнешниеКомпонентыСлужебныйКлиент.ПроверитьДоступностьКомпоненты(Оповещение,
					КонтекстПроверкиКомпоненты);
			Иначе
				ВыполнитьОбработкуОповещения(Оповещение, Неопределено);
			КонецЕсли;
			Возврат;
		КонецЕсли;
	КонецЕсли;
	
	// Генерация уникального имени.
	СимволическоеИмя = "С" + СтрЗаменить(Строка(Новый УникальныйИдентификатор), "-", "");
	Контекст.Вставить("СимволическоеИмя", СимволическоеИмя);
	
	Оповещение = Новый ОписаниеОповещения(
		"ПодключитьКомпонентуПослеПопыткиПодключения", ЭтотОбъект, Контекст,
		"ПодключитьКомпонентуПриОбработкеОшибки", ЭтотОбъект);
	
	#Если МобильноеПриложениеКлиент Или МобильныйКлиент Тогда
	НачатьПодключениеВнешнейКомпоненты(Оповещение, Контекст.Местоположение, СимволическоеИмя);
	#Иначе
	НачатьПодключениеВнешнейКомпоненты(Оповещение, Контекст.Местоположение, СимволическоеИмя,,
		ОбщегоНазначенияСлужебныйКлиентСервер.ТипПодключенияКомпоненты(Контекст.Изолированно));
	#КонецЕсли
	
КонецПроцедуры

// Продолжение процедуры ПодключитьКомпоненту.
Процедура ПодключитьКомпонентуОповеститьОПодключении(ПодключаемыйМодуль, Контекст) Экспорт
	
	Результат = РезультатПодключенияКомпоненты();
	Результат.Подключено = Истина;
	Результат.ПодключаемыйМодуль = ПодключаемыйМодуль;
	ВыполнитьОбработкуОповещения(Контекст.Оповещение, Результат);
	
КонецПроцедуры

// Продолжение процедуры ПодключитьКомпоненту.
Процедура ПодключитьКомпонентуОповеститьОбОшибке(ОписаниеОшибки, Контекст, РегистрироватьОшибкуВЖурнале = Истина) Экспорт
	
	Если Не ПустаяСтрока(ОписаниеОшибки) И РегистрироватьОшибкуВЖурнале Тогда
		ЖурналРегистрацииКлиент.ДобавитьСообщениеДляЖурналаРегистрации(
			НСтр("ru = 'Подключение внешней компоненты на клиенте'", ОбщегоНазначенияКлиент.КодОсновногоЯзыка()),
			"Ошибка", ОписаниеОшибки,, Истина);
	КонецЕсли;
		
	Оповещение = Контекст.Оповещение;
	Результат = РезультатПодключенияКомпоненты();
	Результат.ОписаниеОшибки = ОписаниеОшибки;
	ВыполнитьОбработкуОповещения(Оповещение, Результат);
	
КонецПроцедуры

// Продолжение процедуры ПодключитьКомпоненту.
Функция РезультатПодключенияКомпоненты() Экспорт
	
	Результат = Новый Структура;
	Результат.Вставить("Подключено", Ложь);
	Результат.Вставить("ОписаниеОшибки", "");
	Результат.Вставить("ПодключаемыйМодуль", Неопределено);
	
	Возврат Результат;
	
КонецФункции

Функция ОшибкаПодключенияКомпоненты(ОписаниеОшибки, РегистрироватьОшибкуВЖурнале = Истина) Экспорт
	
	Если Не ПустаяСтрока(ОписаниеОшибки) И РегистрироватьОшибкуВЖурнале Тогда
		ЖурналРегистрацииКлиент.ДобавитьСообщениеДляЖурналаРегистрации(
			НСтр("ru = 'Подключение внешней компоненты на клиенте'", ОбщегоНазначенияКлиент.КодОсновногоЯзыка()),
			"Ошибка", ОписаниеОшибки,, Истина);
	КонецЕсли;
		
	Результат = РезультатПодключенияКомпоненты();
	Результат.ОписаниеОшибки = ОписаниеОшибки;
	Возврат Результат;
	
КонецФункции

// Параметры:
//  Контекст - Структура:
//      * Оповещение     - ОписаниеОповещения
//      * Местоположение - Строка
//      * ТекстПояснения - Строка
//      * Идентификатор - Строка
//
Процедура УстановитьКомпоненту(Контекст) Экспорт
	
	ПроверитьМестоположениеКомпоненты(Контекст.Идентификатор, Контекст.Местоположение);
	
	// Проверка факта подключения внешней компоненты в этом сеансе ранее.
	СимволическоеИмя = ПолучитьСимволическоеИмяКомпонентыИзКэша(Контекст.Местоположение);
	
	Если СимволическоеИмя = Неопределено Тогда
		
		Оповещение = Новый ОписаниеОповещения(
			"УстановитьКомпонентуПослеОтветаНаВопросОбУстановке", ЭтотОбъект, Контекст);
		
		ПараметрыФормы = Новый Структура;
		ПараметрыФормы.Вставить("ТекстПояснения", Контекст.ТекстПояснения);
		
		ОткрытьФорму("ОбщаяФорма.ВопросОбУстановкеВнешнейКомпоненты", 
			ПараметрыФормы,,,,, Оповещение);
		
	Иначе 
		
		// Если в кэше уже есть символическое имя - значит к этому сеансу ранее компонента уже подключалась,
		// значит внешняя компонента уже установлена.
		Результат = РезультатУстановкиКомпоненты();
		Результат.Вставить("Установлено", Истина);
		ВыполнитьОбработкуОповещения(Контекст.Оповещение, Результат);
		
	КонецЕсли;
	
КонецПроцедуры

// Продолжение процедуры УстановитьКомпоненту.
Процедура УстановитьКомпонентуОповеститьОбОшибке(ОписаниеОшибки, Контекст) Экспорт
	
	Оповещение = Контекст.Оповещение;
	
	Результат = РезультатУстановкиКомпоненты();
	Результат.ОписаниеОшибки = ОписаниеОшибки;
	ВыполнитьОбработкуОповещения(Оповещение, Результат);
	
КонецПроцедуры

// Параметры:
//  Контекст - см. КонтекстПодключенияКомпоненты.
//
Асинх Функция УстановитьКомпонентуАсинх(Контекст) Экспорт
	
	ПроверитьМестоположениеКомпоненты(Контекст.Идентификатор, Контекст.Местоположение);
	
	// Проверка факта подключения внешней компоненты в этом сеансе ранее.
	СимволическоеИмя = ПолучитьСимволическоеИмяКомпонентыИзКэша(Контекст.Местоположение);
	
	Если СимволическоеИмя = Неопределено Тогда 
		
		ТекстПояснения =  ?(ПустаяСтрока(Контекст.ТекстПояснения),
			НСтр("ru = 'Установить внешнюю компоненту?'"), Контекст.ТекстПояснения);
		
		СписокКнопок = Новый СписокЗначений;
		СписокКнопок.Добавить(КодВозвратаДиалога.Да,  НСтр("ru = 'Установить и продолжить'"));
		СписокКнопок.Добавить(КодВозвратаДиалога.Нет, НСтр("ru = 'Отмена'"));
		
		Ответ = Ждать ВопросАсинх(ТекстПояснения, СписокКнопок,,
			КодВозвратаДиалога.Да, НСтр("ru = 'Установка внешней компоненты'"));

		Если Ответ = КодВозвратаДиалога.Да Тогда
			Попытка
				
				Ждать УстановитьВнешнююКомпонентуАсинх(Контекст.Местоположение);
				Результат = РезультатУстановкиКомпоненты();
				Результат.Установлено = Истина;
				Возврат Результат;
				
			Исключение
				
				ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось установить внешнюю компоненту ""%1"" на клиенте 
					           |%2
					           |по причине:
					           |%3'"),
					Контекст.Идентификатор,
					Контекст.Местоположение,
					ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке()));
					
				Возврат ОшибкаУстановкиКомпоненты(ТекстОшибки);
				
			КонецПопытки;
		Иначе
			Возврат РезультатУстановкиКомпоненты();
		КонецЕсли;
	Иначе 
		
		// Если в кэше уже есть символическое имя - значит к этому сеансу ранее компонента уже подключалась,
		// значит внешняя компонента уже установлена.
		
		Результат = РезультатУстановкиКомпоненты();
		Результат.Установлено = Истина;
		Возврат Результат;
		
	КонецЕсли;

КонецФункции

Функция ОшибкаУстановкиКомпоненты(ОписаниеОшибки) Экспорт
	
	Результат = РезультатУстановкиКомпоненты();
	Результат.ОписаниеОшибки = ОписаниеОшибки;
	Возврат Результат;
	
КонецФункции

#КонецОбласти

#Область ТабличныйДокумент

////////////////////////////////////////////////////////////////////////////////
// Функции для работы с табличными документами.

// Выполняет расчет и вывод показателей выделенных областей ячеек табличного документа.
//
// Параметры:
//  Форма - ФормаКлиентскогоПриложения - форма, в которой выводятся значения расчетных показателей.
//  ИмяТабличногоДокумента - Строка - имя реквизита формы типа ТабличныйДокумент, показатели которого рассчитываются.
//  ТекущаяКоманда - Строка - имя команды расчета показателя, например, "РассчитатьСумму".
//                            Определяет, какой показатель является основным.
//  МинимальноеКоличество - Число
//
Процедура РассчитатьПоказатели(Форма, ИмяТабличногоДокумента, ТекущаяКоманда = "", МинимальноеКоличество = 0) Экспорт 
	
	Элементы = Форма.Элементы;
	ТабличныйДокумент = Форма[ИмяТабличногоДокумента];
	ПолеТабличногоДокумента = Элементы[ИмяТабличногоДокумента];
	
	Если Не ЗначениеЗаполнено(ТекущаяКоманда) Тогда 
		ТекущаяКоманда = ТекущаяКомандаРасчетаПоказателей(Элементы);
	КонецЕсли;
	
	ПараметрыРасчета = ОбщегоНазначенияКлиентСервер.ПараметрыРасчетаПоказателейЯчеек(ПолеТабличногоДокумента);
	
	Если ПараметрыРасчета.РассчитатьНаСервере Тогда 
		
		ДлительнаяОперация = СтандартныеПодсистемыВызовСервера.РасчетныеПоказателиЯчеек(
			ТабличныйДокумент, ПараметрыРасчета.ВыделенныеОбласти, Форма.УникальныйИдентификатор);
		
		Если ДлительнаяОперация = Неопределено Тогда
			Возврат;
		КонецЕсли;
		
		ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(Форма);
		ПараметрыОжидания.ФормаВладелец = Форма;
		ПараметрыОжидания.ВыводитьОкноОжидания = Ложь;
		
		ДополнительныеПараметры = Новый Структура;
		ДополнительныеПараметры.Вставить("Форма", Форма);
		ДополнительныеПараметры.Вставить("ТекущаяКоманда", ТекущаяКоманда);
		ДополнительныеПараметры.Вставить("МинимальноеКоличество", МинимальноеКоличество);
		
		ОповещениеОЗавершении = Новый ОписаниеОповещения("ПродолжитьРасчетПоказателей", ЭтотОбъект, ДополнительныеПараметры);
		ДлительныеОперацииКлиент.ОжидатьЗавершение(ДлительнаяОперация, ОповещениеОЗавершении, ПараметрыОжидания);
		
	Иначе
		
		РасчетныеПоказатели = ОбщегоНазначенияКлиентСервер.РасчетныеПоказателиЯчеек(
			ТабличныйДокумент, ПолеТабличногоДокумента, ПараметрыРасчета);
		
		ЗавершитьРасчетПоказателей(Форма, ТекущаяКоманда, МинимальноеКоличество, РасчетныеПоказатели);
		
	КонецЕсли;
	
КонецПроцедуры

// Управляет признаком видимости панели расчетных показателей.
//
// Параметры:
//  ЭлементыФормы - ЭлементыФормы
//  Видимость - Булево - признак включения / выключения видимости панели показателей.
//              См. также Синтакс-помощник: ГруппаФормы.Видимость.
//
Процедура УстановитьВидимостьПанелиПоказателей(ЭлементыФормы, Видимость = Ложь) Экспорт 
	
	ЭлементыФормы.ОбластьПоказателей.Видимость = Видимость;
	ИзменитьСвойствоЭлементаРасчетаПоказателей(ЭлементыФормы, "РассчитатьВсеПоказатели", "Пометка", Видимость);
	
КонецПроцедуры

#КонецОбласти

// Сокращает имя файла с расширением.
// Сокращение имени, производится, когда имя файла с расширением превышает размер 255 байт.
// Сокращение ими файла достигая за счет сокращения имени файла без расширения и 
// добавления значения хеш функции сокращенной строки.
// 
// Параметры:
//  ИмяФайла - Строка - имя файла с расширением
//
Процедура СократитьИмяФайла(ИмяФайла) Экспорт

	ОграничениеВБайтах =  255;  
	Если РазмерСтрокиВБайтах(ИмяФайла) <= ОграничениеВБайтах Тогда
		Возврат;
	КонецЕсли;
	
	Файл = Новый Файл(ИмяФайла);
	ИмяБезРасширения = Файл.ИмяБезРасширения;
	
	ОграничениеВБайтах = ОграничениеВБайтах - РазмерСтрокиВБайтах(Файл.Расширение);
	
	ДлинаСтроки = СтрДлина(ИмяБезРасширения);
	ИспользуемоеКоличествоСимволов = ОграничениеВБайтах - 32;
	БольшееКоличествоСимволов = Мин(ДлинаСтроки, ОграничениеВБайтах);
	МеньшееКоличествоСимволов = Цел((ОграничениеВБайтах - 32)/4);
	Уменьшить = Истина;
	Пока Истина Цикл
		Если Уменьшить Тогда
			ИспользуемоеКоличествоСимволов = МеньшееКоличествоСимволов + Цел((ИспользуемоеКоличествоСимволов-МеньшееКоличествоСимволов)/2);
		Иначе
			ИспользуемоеКоличествоСимволов = ИспользуемоеКоличествоСимволов + Цел((БольшееКоличествоСимволов - ИспользуемоеКоличествоСимволов)/2);
		КонецЕсли;
		
		РазмерСтрокиВБайтах = РазмерСтрокиВБайтах(ИмяБезРасширения)+32;
		
		Если РазмерСтрокиВБайтах = ОграничениеВБайтах Или БольшееКоличествоСимволов - МеньшееКоличествоСимволов = 1 Тогда
			Прервать;
		КонецЕсли;
		
		Если РазмерСтрокиВБайтах > ОграничениеВБайтах Тогда
			Уменьшить = Истина;
			БольшееКоличествоСимволов = ИспользуемоеКоличествоСимволов;	
		Иначе 
			Уменьшить = Ложь;
			МеньшееКоличествоСимволов = ИспользуемоеКоличествоСимволов;
		КонецЕсли;

	КонецЦикла;
	
	СокращеннаяСтрока = СократитьСтроку(ИмяБезРасширения, ИспользуемоеКоличествоСимволов);
	ИмяФайла = СокращеннаяСтрока + Файл.Расширение;
		
КонецПроцедуры

#Область Прочее

// Конструктор параметров для открытия формы выбора формата печатных форм.
// 
// Возвращаемое значение:
//  Структура - настройки формата печатной формы:
//   * УпаковатьВАрхив - Булево - признак необходимости упаковки вложений в архив;
//   * ФорматыСохранения - Массив из см. СтандартныеПодсистемыСервер.НастройкиФорматовСохраненияТабличногоДокумента
//   * Получатели - Массив из Структура:
//                            * ИсточникКонтактнойИнформации - СправочникСсылка - владелец контактной информации.
//                            * Адрес - Строка - почтовый адрес получателя сообщения.
//                            * Представление - Строка - представление адресата.
//   * ПереводитьИменаФайловВТранслит - Булево - преобразовывать кириллические символы в латиницу; 
//   * Подписать  - Неопределено, 
// 				- Булево - признак необходимости подписания печатных форм электронной подписью, если Неопределено, то флажок не отображается в форме выбора формата.
//   * ПодписьИПечать - Булево - признак использования факсимиле подписи и печати.
//
Функция НастройкиФорматаПечатнойФормы() Экспорт
	Результат = Новый Структура;
	Результат.Вставить("УпаковатьВАрхив", Ложь);
	Результат.Вставить("ФорматыСохранения", Новый Массив);
	Результат.Вставить("Получатели", Новый Массив);
	Результат.Вставить("ПереводитьИменаФайловВТранслит", Ложь);
	Результат.Вставить("Подписать", Неопределено);
	Результат.Вставить("ПодписьИПечать", Ложь);
	Возврат Результат;
КонецФункции

#КонецОбласти

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

#Область Данные

#Область СкопироватьРекурсивно

Функция СкопироватьСтруктуру(СтруктураИсточник, ФиксироватьДанные) Экспорт 
	
	СтруктураРезультат = Новый Структура;
	
	Для Каждого КлючИЗначение Из СтруктураИсточник Цикл
		СтруктураРезультат.Вставить(КлючИЗначение.Ключ, 
			ОбщегоНазначенияКлиент.СкопироватьРекурсивно(КлючИЗначение.Значение, ФиксироватьДанные));
	КонецЦикла;
	
	Если ФиксироватьДанные = Истина 
		Или ФиксироватьДанные = Неопределено
		И ТипЗнч(СтруктураИсточник) = Тип("ФиксированнаяСтруктура") Тогда 
		Возврат Новый ФиксированнаяСтруктура(СтруктураРезультат);
	КонецЕсли;
	
	Возврат СтруктураРезультат;
	
КонецФункции

Функция СкопироватьСоответствие(СоответствиеИсточник, ФиксироватьДанные) Экспорт 
	
	СоответствиеРезультат = Новый Соответствие;
	
	Для Каждого КлючИЗначение Из СоответствиеИсточник Цикл
		СоответствиеРезультат.Вставить(КлючИЗначение.Ключ, 
			ОбщегоНазначенияКлиент.СкопироватьРекурсивно(КлючИЗначение.Значение, ФиксироватьДанные));
	КонецЦикла;
	
	Если ФиксироватьДанные = Истина 
		Или ФиксироватьДанные = Неопределено
		И ТипЗнч(СоответствиеИсточник) = Тип("ФиксированноеСоответствие") Тогда 
		Возврат Новый ФиксированноеСоответствие(СоответствиеРезультат);
	КонецЕсли;
	
	Возврат СоответствиеРезультат;
	
КонецФункции

Функция СкопироватьМассив(МассивИсточник, ФиксироватьДанные) Экспорт 
	
	МассивРезультат = Новый Массив;
	
	Для Каждого Элемент Из МассивИсточник Цикл
		МассивРезультат.Добавить(ОбщегоНазначенияКлиент.СкопироватьРекурсивно(Элемент, ФиксироватьДанные));
	КонецЦикла;
	
	Если ФиксироватьДанные = Истина 
		Или ФиксироватьДанные = Неопределено
		И ТипЗнч(МассивИсточник) = Тип("ФиксированныйМассив") Тогда 
		Возврат Новый ФиксированныйМассив(МассивРезультат);
	КонецЕсли;
	
	Возврат МассивРезультат;
	
КонецФункции

Функция СкопироватьСписокЗначений(СписокИсточник, ФиксироватьДанные) Экспорт
	
	СписокРезультат = Новый СписокЗначений;
	
	Для Каждого ЭлементСписка Из СписокИсточник Цикл
		СписокРезультат.Добавить(
			ОбщегоНазначенияКлиент.СкопироватьРекурсивно(ЭлементСписка.Значение, ФиксироватьДанные), 
			ЭлементСписка.Представление, 
			ЭлементСписка.Пометка, 
			ЭлементСписка.Картинка);
	КонецЦикла;
	
	Возврат СписокРезультат;
	
КонецФункции

#КонецОбласти

#КонецОбласти

#Область Формы

Функция ИмяОбъектаМетаданных(Тип) Экспорт
	
	ИмяПараметра = "СтандартныеПодсистемы.ИменаОбъектовМетаданных";
	Если ПараметрыПриложения[ИмяПараметра] = Неопределено Тогда
		ПараметрыПриложения.Вставить(ИмяПараметра, Новый Соответствие);
	КонецЕсли;
	ИменаОбъектовМетаданных = ПараметрыПриложения[ИмяПараметра];
	
	Результат = ИменаОбъектовМетаданных[Тип];
	Если Результат = Неопределено Тогда
		Результат = СтандартныеПодсистемыВызовСервера.ИмяОбъектаМетаданных(Тип);
		ИменаОбъектовМетаданных.Вставить(Тип, Результат);
	КонецЕсли;
	
	Возврат Результат;
	
КонецФункции

Процедура ПодтвердитьЗакрытиеФормы() Экспорт
	
	ИмяПараметра = "СтандартныеПодсистемы.ПараметрыПодтвержденияЗакрытияФормы";
	Если ПараметрыПриложения[ИмяПараметра] = Неопределено Тогда
		ПараметрыПриложения.Вставить(ИмяПараметра, Неопределено);
	КонецЕсли;
	
	Параметры = ПараметрыПриложения["СтандартныеПодсистемы.ПараметрыПодтвержденияЗакрытияФормы"];
	Если Параметры = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Оповещение = Новый ОписаниеОповещения("ПодтвердитьЗакрытиеФормыЗавершение", ЭтотОбъект, Параметры);
	Если ПустаяСтрока(Параметры.ТекстПредупреждения) Тогда
		ТекстВопроса = НСтр("ru = 'Данные были изменены. Сохранить изменения?'");
	Иначе
		ТекстВопроса = Параметры.ТекстПредупреждения;
	КонецЕсли;
	
	ПоказатьВопрос(Оповещение, ТекстВопроса, РежимДиалогаВопрос.ДаНетОтмена, ,
		КодВозвратаДиалога.Да);
	
КонецПроцедуры

Процедура ПодтвердитьЗакрытиеФормыЗавершение(Ответ, Параметры) Экспорт
	
	ПараметрыПриложения["СтандартныеПодсистемы.ПараметрыПодтвержденияЗакрытияФормы"] = Неопределено;
	
	Если Ответ = КодВозвратаДиалога.Да Тогда
		ВыполнитьОбработкуОповещения(Параметры.ОповещениеСохранитьИЗакрыть);
		
	ИначеЕсли Ответ = КодВозвратаДиалога.Нет Тогда
		Форма = Параметры.ОповещениеСохранитьИЗакрыть.Модуль;
		Форма.Модифицированность = Ложь;
		Форма.Закрыть();
	Иначе
		Форма = Параметры.ОповещениеСохранитьИЗакрыть.Модуль;
		Форма.Модифицированность = Истина;
	КонецЕсли;
	
КонецПроцедуры

Процедура ПодтвердитьЗакрытиеПроизвольнойФормы() Экспорт
	
	ИмяПараметра = "СтандартныеПодсистемы.ПараметрыПодтвержденияЗакрытияФормы";
	Если ПараметрыПриложения[ИмяПараметра] = Неопределено Тогда
		ПараметрыПриложения.Вставить(ИмяПараметра, Неопределено);
	КонецЕсли;
	
	Параметры = ПараметрыПриложения["СтандартныеПодсистемы.ПараметрыПодтвержденияЗакрытияФормы"];
	Если Параметры = Неопределено Тогда
		Возврат;
	КонецЕсли;
	ПараметрыПриложения["СтандартныеПодсистемы.ПараметрыПодтвержденияЗакрытияФормы"] = Неопределено;
	РежимВопроса = РежимДиалогаВопрос.ДаНет;
	
	Оповещение = Новый ОписаниеОповещения("ПодтвердитьЗакрытиеПроизвольнойФормыЗавершение", ЭтотОбъект, Параметры);
	
	ПоказатьВопрос(Оповещение, Параметры.ТекстПредупреждения, РежимВопроса);
	
КонецПроцедуры

Процедура ПодтвердитьЗакрытиеПроизвольнойФормыЗавершение(Ответ, Параметры) Экспорт
	
	Форма = Параметры.Форма;
	Если Ответ = КодВозвратаДиалога.Да
		Или Ответ = КодВозвратаДиалога.ОК Тогда
		Форма[Параметры.ИмяРеквизитаЗакрытьФормуБезПодтверждения] = Истина;
		Если Параметры.ОписаниеОповещенияЗакрыть <> Неопределено Тогда
			ВыполнитьОбработкуОповещения(Параметры.ОписаниеОповещенияЗакрыть);
		КонецЕсли;
		Форма.Закрыть();
	Иначе
		Форма[Параметры.ИмяРеквизитаЗакрытьФормуБезПодтверждения] = Ложь;
	КонецЕсли;
	
КонецПроцедуры

#КонецОбласти

#Область ФормыРедактирования

Процедура КомментарийЗавершениеВвода(Знач ВведенныйТекст, Знач ДополнительныеПараметры) Экспорт
	
	Если ВведенныйТекст = Неопределено Тогда
		Возврат;
	КонецЕсли;	
	
	РеквизитФормы = ДополнительныеПараметры.ФормаВладелец;
	
	ПутьКРеквизитуФормы = СтрРазделить(ДополнительныеПараметры.ИмяРеквизита, ".");
	// Если реквизит вида "Объект.Комментарий" и т.п.
	Если ПутьКРеквизитуФормы.Количество() > 1 Тогда
		Для Индекс = 0 По ПутьКРеквизитуФормы.Количество() - 2 Цикл 
			РеквизитФормы = РеквизитФормы[ПутьКРеквизитуФормы[Индекс]];
		КонецЦикла;
	КонецЕсли;	
	
	РеквизитФормы[ПутьКРеквизитуФормы[ПутьКРеквизитуФормы.Количество() - 1]] = ВведенныйТекст;
	ДополнительныеПараметры.ФормаВладелец.Модифицированность = Истина;
	
КонецПроцедуры

#КонецОбласти

#Область ВнешниеКомпоненты

// Продолжение процедуры ПодключитьКомпоненту.
// 
// Параметры:
//  Результат - Неопределено - если нет подсистемы ВнешниеКомпоненты
//            - см. ВнешниеКомпонентыСлужебныйКлиент.РезультатДоступностиКомпоненты
//  Контекст - см. ОбщегоНазначенияСлужебныйКлиент.КонтекстПодключенияКомпоненты
//
Процедура ПодключитьПослеПоискаВнешнейКомпоненты(Результат, Контекст) Экспорт
	
	Контекст.ВыполненПоискНовойВерсии = Истина;
	Если Результат = Неопределено Или Результат.КомпонентаПоследнейВерсии = Неопределено Тогда
		КомпонентаПоследнейВерсии = СтандартныеПодсистемыВызовСервера.КомпонентаПоследнейВерсии(
			Контекст.Идентификатор, Контекст.ИсходноеМестоположение, Результат);
	Иначе
		КомпонентаПоследнейВерсии = Результат.КомпонентаПоследнейВерсии;
	КонецЕсли;
	
	Контекст.Местоположение = КомпонентаПоследнейВерсии.Местоположение;
	Контекст.Версия = КомпонентаПоследнейВерсии.Версия;
	ПодключитьКомпоненту(Контекст);
	
КонецПроцедуры

// Продолжение процедуры ПодключитьКомпоненту.
Процедура ПодключитьКомпонентуПослеПопыткиПодключения(Подключено, Контекст) Экспорт 
	
	Если Подключено Тогда 
		
		// Сохранение факта подключения внешней компоненты к этому сеансу.
		
		ИсходноеМестоположение = ?(ЗначениеЗаполнено(Контекст.ИсходноеМестоположение),
			Контекст.ИсходноеМестоположение, Контекст.Местоположение);
		
		ЗаписатьСимволическоеИмяКомпонентыВКэш(ИсходноеМестоположение, Контекст.СимволическоеИмя);
		
		ПодключаемыйМодуль = Неопределено;
		
		Попытка
			ПодключаемыйМодуль = НовыйОбъектКомпоненты(Контекст);
		Исключение
			// Текст ошибки уже скомпонован в НовыйОбъектКомпоненты, требуется только оповестить.
			ТекстОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
			ПодключитьКомпонентуОповеститьОбОшибке(ТекстОшибки, Контекст);
			Возврат;
		КонецПопытки;
		
		Если Контекст.Кэшировать Тогда 
			ЗаписатьОбъектКомпонентыВКэш(ИсходноеМестоположение, ПодключаемыйМодуль)
		КонецЕсли;
		
		ПодключитьКомпонентуОповеститьОПодключении(ПодключаемыйМодуль, Контекст);
		
	Иначе 
		
		Если Контекст.ПредложитьУстановить И Не Контекст.БылаПопыткаУстановки Тогда 
			ПодключитьКомпонентуНачатьУстановку(Контекст);
		ИначеЕсли Не Контекст.ПредложитьУстановить Тогда
			Оповещение = Контекст.Оповещение;
			Результат = РезультатПодключенияКомпоненты();
			ТекстОшибки =  СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось подключить внешнюю компоненту ""%1"".
				|Возможно компонента не установлена.'"), Контекст.Идентификатор);
			
			Результат.ОписаниеОшибки = ТекстОшибки;
			
			ВыполнитьОбработкуОповещения(Оповещение, Результат);
		Иначе 
			ТекстОшибки =  СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось подключить внешнюю компоненту ""%1"".
				   |Возможно компонента не предназначена для клиентского приложения %2.
				   |
				   |Техническая информация:
				   |%3
				   |Метод %4 вернул Ложь.'"),
				Контекст.Идентификатор, ВидПриложения(), Контекст.Местоположение, "НачатьПодключениеВнешнейКомпоненты");
				
			ПодключитьКомпонентуОповеститьОбОшибке(ТекстОшибки, Контекст, Контекст.ПредложитьУстановить);
		КонецЕсли;
		
	КонецЕсли;
	
КонецПроцедуры

Функция ВидПриложения()

	СистемнаяИнформация = Новый СистемнаяИнформация();
	Результат = "";
#Если ВебКлиент Тогда
	Результат = НСтр("ru = 'Веб-клиент'") + СистемнаяИнформация.ИнформацияПрограммыПросмотра;
#ИначеЕсли ТолстыйКлиентОбычноеПриложение Или ТолстыйКлиентУправляемоеПриложение Тогда
	Результат = НСтр("ru = 'Толстый клиент'");
#ИначеЕсли ТонкийКлиент Тогда
	Результат = НСтр("ru = 'Тонкий клиент'");
#КонецЕсли
	Возврат Результат + " (" + СистемнаяИнформация.ТипПлатформы + ")";

КонецФункции

// Продолжение процедуры ПодключитьКомпоненту.
Процедура ПодключитьКомпонентуНачатьУстановку(Контекст)
	
	Оповещение = Новый ОписаниеОповещения(
		"ПодключитьКомпонентуПослеУстановки", ЭтотОбъект, Контекст);
	
	КонтекстУстановки = Новый Структура;
	КонтекстУстановки.Вставить("Оповещение", Оповещение);
	КонтекстУстановки.Вставить("Местоположение", Контекст.Местоположение);
	КонтекстУстановки.Вставить("ТекстПояснения", Контекст.ТекстПояснения);
	КонтекстУстановки.Вставить("Идентификатор", Контекст.Идентификатор);
	
	УстановитьКомпоненту(КонтекстУстановки);
	
КонецПроцедуры

// Продолжение процедуры ПодключитьКомпоненту.
Процедура ПодключитьКомпонентуПослеУстановки(Результат, Контекст) Экспорт 
	
	Если Результат.Установлено Тогда 
		// Одна попытка установки уже прошла, если компонента не подключится в этот раз,
		// то и предлагать ее установить еще раз не следует.
		Контекст.БылаПопыткаУстановки = Истина;
		ПодключитьКомпоненту(Контекст);
	Иначе 
		// Расшифровка ОписаниеОшибки не нужна, текст уже сформирован при установке.
		// При отказе от установки пользователем ОписаниеОшибки - пустая строка.
		ПодключитьКомпонентуОповеститьОбОшибке(Результат.ОписаниеОшибки, Контекст);
	КонецЕсли;
	
КонецПроцедуры

// Продолжение процедуры ПодключитьКомпоненту.
// 
// Параметры:
//  ИнформацияОбОшибке- ИнформацияОбОшибке
//  СтандартнаяОбработка - Булево
//  Контекст - см. ПодключитьКомпоненту.Контекст
//
Процедура ПодключитьКомпонентуПриОбработкеОшибки(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт
	
	СтандартнаяОбработка = Ложь;
	
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Не удалось подключить внешнюю компоненту ""%1"" на клиенте
		           |%2
		           |по причине:
		           |%3'"),
		Контекст.Идентификатор,
		Контекст.Местоположение,
		ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
		
	ПодключитьКомпонентуОповеститьОбОшибке(ТекстОшибки, Контекст);
	
КонецПроцедуры

// Создает экземпляр внешней компоненты (или несколько)
Функция НовыйОбъектКомпоненты(Контекст)
	
	КомпонентаСодержитЕдинственныйКлассОбъектов = (Контекст.ИдентификаторыСозданияОбъектов.Количество() = 0);
	
	Если КомпонентаСодержитЕдинственныйКлассОбъектов Тогда 
		
		Попытка
			ПодключаемыйМодуль = Новый("AddIn." + Контекст.СимволическоеИмя + "." + Контекст.Идентификатор);
			Если ПодключаемыйМодуль = Неопределено Тогда 
				ВызватьИсключение НСтр("ru = 'Оператор Новый вернул Неопределено'");
			КонецЕсли;
		Исключение
			ПодключаемыйМодуль = Неопределено;
			ТекстОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
		КонецПопытки;
		
		Если ПодключаемыйМодуль = Неопределено Тогда 
			
			ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось создать объект внешней компоненты ""%1"", подключенной на клиенте
				           |%2,
				           |по причине:
				           |%3'"),
				Контекст.Идентификатор,
				Контекст.Местоположение,
				ТекстОшибки);
			
		КонецЕсли;
		
	Иначе 
		
		ПодключаемыеМодули = Новый Соответствие;
		Для каждого ИдентификаторОбъекта Из Контекст.ИдентификаторыСозданияОбъектов Цикл 
			
			Попытка
				ПодключаемыйМодуль = Новый("AddIn." + Контекст.СимволическоеИмя + "." + ИдентификаторОбъекта);
				Если ПодключаемыйМодуль = Неопределено Тогда 
					ВызватьИсключение НСтр("ru = 'Оператор Новый вернул Неопределено'");
				КонецЕсли;
			Исключение
				ПодключаемыйМодуль = Неопределено;
				ТекстОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
			КонецПопытки;
			
			Если ПодключаемыйМодуль = Неопределено Тогда 
				
				ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = 'Не удалось создать объект ""%1"" внешней компоненты ""%2"", подключенной на клиенте
					           |%3,
					           |по причине:
					           |%4'"),
					ИдентификаторОбъекта,
					Контекст.Идентификатор,
					Контекст.Местоположение,
					ТекстОшибки);
				
			КонецЕсли;
			
			ПодключаемыеМодули.Вставить(ИдентификаторОбъекта, ПодключаемыйМодуль);
			
		КонецЦикла;
		
		ПодключаемыйМодуль = Новый ФиксированноеСоответствие(ПодключаемыеМодули);
		
	КонецЕсли;
	
	Возврат ПодключаемыйМодуль;
	
КонецФункции

// Продолжение процедуры УстановитьКомпоненту.
Процедура УстановитьКомпонентуПослеОтветаНаВопросОбУстановке(Ответ, Контекст) Экспорт
	
	// Результат: 
	// - КодВозвратаДиалога.Да - Установить.
	// - КодВозвратаДиалога.Отмена - Отклонить.
	// - Неопределено - Закрыто окно.
	Если Ответ = КодВозвратаДиалога.Да Тогда
		УстановитьКомпонентуНачатьУстановку(Контекст);
	Иначе
		Результат = РезультатУстановкиКомпоненты();
		ВыполнитьОбработкуОповещения(Контекст.Оповещение, Результат);
	КонецЕсли;
	
КонецПроцедуры

// Продолжение процедуры УстановитьКомпоненту.
Процедура УстановитьКомпонентуНачатьУстановку(Контекст)
	
	Оповещение = Новый ОписаниеОповещения(
		"УстановитьКомпонентуПослеПопыткиУстановки", ЭтотОбъект, Контекст,
		"УстановитьКомпонентуПриОбработкеОшибки", ЭтотОбъект);
	
	НачатьУстановкуВнешнейКомпоненты(Оповещение, Контекст.Местоположение);
	
КонецПроцедуры

// Продолжение процедуры УстановитьКомпоненту.
Процедура УстановитьКомпонентуПослеПопыткиУстановки(Контекст) Экспорт 
	
	Результат = РезультатУстановкиКомпоненты();
	Результат.Вставить("Установлено", Истина);
	
	ВыполнитьОбработкуОповещения(Контекст.Оповещение, Результат);
	
КонецПроцедуры

// Продолжение процедуры УстановитьКомпоненту.
// 
// Параметры:
//  ИнформацияОбОшибке - ИнформацияОбОшибке
//  СтандартнаяОбработка - Булево
//  Контекст - см. ПодключитьКомпоненту.Контекст 
// 
Процедура УстановитьКомпонентуПриОбработкеОшибки(ИнформацияОбОшибке, СтандартнаяОбработка, Контекст) Экспорт
	
	СтандартнаяОбработка = Ложь;
	
	ТекстОшибки = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = 'Не удалось установить внешнюю компоненту ""%1"" на клиенте 
		           |%2
		           |по причине:
		           |%3'"),
		Контекст.Идентификатор,
		Контекст.Местоположение,
		ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке));
	
	Результат = РезультатУстановкиКомпоненты();
	Результат.ОписаниеОшибки = ТекстОшибки;
	
	ВыполнитьОбработкуОповещения(Контекст.Оповещение, Результат);
	
КонецПроцедуры

// Продолжение процедуры УстановитьКомпоненту.
Функция РезультатУстановкиКомпоненты()
	
	Результат = Новый Структура;
	Результат.Вставить("Установлено", Ложь);
	Результат.Вставить("ОписаниеОшибки", "");
	
	Возврат Результат;
	
КонецФункции

Процедура ПроверитьМестоположениеКомпоненты(Идентификатор, Местоположение)
	
	Если ЭтоМакет(Местоположение) Тогда
		Возврат;
	КонецЕсли;
	
	Если ОбщегоНазначенияКлиент.ПодсистемаСуществует("СтандартныеПодсистемы.ВнешниеКомпоненты") Тогда
		МодульВнешниеКомпонентыСлужебныйКлиент = ОбщегоНазначенияКлиент.ОбщийМодуль("ВнешниеКомпонентыСлужебныйКлиент");
		МодульВнешниеКомпонентыСлужебныйКлиент.ПроверитьМестоположениеКомпоненты(Идентификатор, Местоположение);
	Иначе
		ВызватьИсключение СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось подключить внешнюю компоненту ""%1"" в клиентском приложении
			           |по причине:
			           |указано некорректное местоположение внешней компоненты
			           |%2'"),
			Идентификатор, Местоположение);
	КонецЕсли;

КонецПроцедуры

Асинх Функция ПодключитьКомпонентуПослеПопыткиПодключенияАсинх(Подключено, Контекст)
	
	Если Подключено Тогда 
		
		// Сохранение факта подключения внешней компоненты к этому сеансу.
		
		ИсходноеМестоположение = ?(ЗначениеЗаполнено(Контекст.ИсходноеМестоположение),
			Контекст.ИсходноеМестоположение, Контекст.Местоположение);
		
		ЗаписатьСимволическоеИмяКомпонентыВКэш(ИсходноеМестоположение, Контекст.СимволическоеИмя);
		
		ПодключаемыйМодуль = Неопределено;
		
		Попытка
			ПодключаемыйМодуль = НовыйОбъектКомпоненты(Контекст);
		Исключение
			// Текст ошибки уже скомпонован в НовыйОбъектКомпоненты, требуется только оповестить.
			ТекстОшибки = ОбработкаОшибок.КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
			Возврат ОшибкаПодключенияКомпоненты(ТекстОшибки);
		КонецПопытки;
		
#Если ВебКлиент Тогда
		СистемнаяИнформация = Новый СистемнаяИнформация;
		Если ОбщегоНазначенияКлиентСервер.СравнитьВерсии(СистемнаяИнформация.ВерсияПриложения, "8.3.24.0") >= 0 Тогда
				Ждать ПаузаАсинх(2);
		КонецЕсли;
#КонецЕсли

		Если Контекст.Кэшировать Тогда 
			ЗаписатьОбъектКомпонентыВКэш(ИсходноеМестоположение, ПодключаемыйМодуль)
		КонецЕсли;
		
		Результат = РезультатПодключенияКомпоненты();
		Результат.Подключено = Истина;
		Результат.ПодключаемыйМодуль = ПодключаемыйМодуль;
		Возврат Результат;
		
	Иначе 
		
		Если Контекст.ПредложитьУстановить И Не Контекст.БылаПопыткаУстановки Тогда 
			
			КонтекстУстановки = Новый Структура;
			КонтекстУстановки.Вставить("Местоположение", Контекст.Местоположение);
			КонтекстУстановки.Вставить("ТекстПояснения", Контекст.ТекстПояснения);
			КонтекстУстановки.Вставить("Идентификатор", Контекст.Идентификатор);   
			РезультатУстановки = Ждать УстановитьКомпонентуАсинх(КонтекстУстановки);
			
			Если РезультатУстановки.Установлено Тогда 
				// Одна попытка установки уже прошла, если компонента не подключится в этот раз,
				// то и предлагать ее установить еще раз не следует.
				Контекст.БылаПопыткаУстановки = Истина;
				Возврат ПодключитьКомпонентуАсинх(Контекст);
			Иначе 
				// Расшифровка ОписаниеОшибки не нужна, текст уже сформирован при установке.
				// При отказе от установки пользователем ОписаниеОшибки - пустая строка.
				
				Возврат ОшибкаПодключенияКомпоненты(РезультатУстановки.ОписаниеОшибки);
			КонецЕсли;
		ИначеЕсли Не Контекст.ПредложитьУстановить Тогда
			Оповещение = Контекст.Оповещение;
			Результат = РезультатПодключенияКомпоненты();
			ТекстОшибки =  СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось подключить внешнюю компоненту ""%1"".
				|Возможно компонента не установлена.'"), Контекст.Идентификатор);
			
			Результат.ОписаниеОшибки = ТекстОшибки;
			
			Возврат Результат;	
		Иначе 
			ТекстОшибки =  СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = 'Не удалось подключить внешнюю компоненту ""%1"".
				   |Возможно компонента не предназначена для клиентского приложения %2.
				   |
				   |Техническая информация:
				   |%3
				   |Метод %4 вернул Ложь.'"),
				Контекст.Идентификатор, ВидПриложения(), Контекст.Местоположение, "ПодключитьВнешнююКомпонентуАсинх");
			
			Возврат ОшибкаПодключенияКомпоненты(ТекстОшибки, Контекст.ПредложитьУстановить);
			
		КонецЕсли;
		
	КонецЕсли;
	 
КонецФункции

Асинх Функция ПаузаАсинх(ВремяВСекундах)
	
	ДатаОкончания = ТекущаяДата() + ВремяВСекундах; // АПК:143 Дата сеанса не используется для проверки интервалов времени
	Пока ТекущаяДата() < ДатаОкончания Цикл         // АПК:143 Дата сеанса не используется для проверки интервалов времени
		Ждать 1;
	КонецЦикла;
	
	Возврат Неопределено;
	
КонецФункции

Функция ЭтоМакет(Местоположение)
	
	Если СтрНайти(Местоположение, "/") > 0 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	ШагиПути = СтрРазделить(Местоположение, ".");
	Количество = ШагиПути.Количество();
	
	Если Не Количество = 2 И Не Количество = 4 Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Если Количество = 2 И Не ВРег(ШагиПути[0]) = "ОБЩИЙМАКЕТ" Тогда
		Возврат Ложь;
	КонецЕсли;

	Если Количество = 4 И Не ВРег(ШагиПути[2]) = "МАКЕТ" Тогда
		Возврат Ложь;
	КонецЕсли;
	
	Для каждого ШагПути Из ШагиПути Цикл
		Если Не ОбщегоНазначенияКлиентСервер.ИмяСоответствуетТребованиямИменованияСвойств(ШагПути) Тогда
			Возврат Ложь;
		КонецЕсли;
	КонецЦикла;
	
	Возврат Истина;
	
КонецФункции

// Получает из кэша символическое имя внешней компоненты, если она была ранее подключена.
Функция ПолучитьСимволическоеИмяКомпонентыИзКэша(КлючОбъекта)
	
	СимволическоеИмя = Неопределено;
	КэшированныеСимволическоеИмена = ПараметрыПриложения["СтандартныеПодсистемы.ВнешниеКомпоненты.СимволическиеИмена"];
	
	Если ТипЗнч(КэшированныеСимволическоеИмена) = Тип("ФиксированноеСоответствие") Тогда
		СимволическоеИмя = КэшированныеСимволическоеИмена.Получить(КлючОбъекта);
	КонецЕсли;
	
	Возврат СимволическоеИмя;
	
КонецФункции

// Записывает в кэш символическое имя внешней компоненты.
Процедура ЗаписатьСимволическоеИмяКомпонентыВКэш(КлючОбъекта, СимволическоеИмя)
	
	Соответствие = Новый Соответствие;
	КэшированныеСимволическоеИмена = ПараметрыПриложения["СтандартныеПодсистемы.ВнешниеКомпоненты.СимволическиеИмена"];
	
	Если ТипЗнч(КэшированныеСимволическоеИмена) = Тип("ФиксированноеСоответствие") Тогда
		
		Если КэшированныеСимволическоеИмена.Получить(КлючОбъекта) <> Неопределено Тогда // Уже есть в кэше.
			Возврат;
		КонецЕсли;
		
		Для каждого Элемент Из КэшированныеСимволическоеИмена Цикл
			Соответствие.Вставить(Элемент.Ключ, Элемент.Значение);
		КонецЦикла;
		
	КонецЕсли;
	
	Соответствие.Вставить(КлючОбъекта, СимволическоеИмя);
	
	ПараметрыПриложения.Вставить("СтандартныеПодсистемы.ВнешниеКомпоненты.СимволическиеИмена",
		Новый ФиксированноеСоответствие(Соответствие));
	
КонецПроцедуры

Функция ПолучитьОбъектКомпонентыИзКэша(КлючОбъекта)
	
	ПодключаемыйМодуль = Неопределено;
	КэшированныеОбъекты = ПараметрыПриложения["СтандартныеПодсистемы.ВнешниеКомпоненты.Объекты"];
	
	Если ТипЗнч(КэшированныеОбъекты) = Тип("ФиксированноеСоответствие") Тогда
		ПодключаемыйМодуль = КэшированныеОбъекты.Получить(КлючОбъекта);
	КонецЕсли;
	
	Возврат ПодключаемыйМодуль;
	
КонецФункции

Процедура ЗаписатьОбъектКомпонентыВКэш(КлючОбъекта, ПодключаемыйМодуль)
	
	Соответствие = Новый Соответствие;
	КэшированныеОбъекты = ПараметрыПриложения["СтандартныеПодсистемы.ВнешниеКомпоненты.Объекты"];
	
	Если ТипЗнч(КэшированныеОбъекты) = Тип("ФиксированноеСоответствие") Тогда
		Для каждого Элемент Из КэшированныеОбъекты Цикл
			Соответствие.Вставить(Элемент.Ключ, Элемент.Значение);
		КонецЦикла;
	КонецЕсли;
	
	Соответствие.Вставить(КлючОбъекта, ПодключаемыйМодуль);
	
	ПараметрыПриложения.Вставить("СтандартныеПодсистемы.ВнешниеКомпоненты.Объекты",
		Новый ФиксированноеСоответствие(Соответствие));
	
КонецПроцедуры

#КонецОбласти

#Область ВнешнееСоединение

// Продолжение процедуры ОбщегоНазначенияКлиент.ЗарегистрироватьCOMСоединитель.
Процедура ЗарегистрироватьCOMСоединительПриПроверкеРегистрации(Результат, Контекст) Экспорт
	
	ПриложениеЗапущено = Результат.ПриложениеЗапущено;
	ОписаниеОшибки = Результат.ОписаниеОшибки;
	КодВозврата = Результат.КодВозврата;
	ВыполнитьПерезагрузкуСеанса = Контекст.ВыполнитьПерезагрузкуСеанса;
	
	Если ПриложениеЗапущено Тогда
		
		Если ВыполнитьПерезагрузкуСеанса Тогда
			
			Оповещение = Новый ОписаниеОповещения("ЗарегистрироватьCOMСоединительПриПроверкеОтветаОПерезапускеСеанса", 
				ОбщегоНазначенияСлужебныйКлиент, Контекст);
			
			ТекстВопроса = 
				НСтр("ru = 'Для завершения перерегистрации компоненты comcntr перезапустите программу.
				           |Перезапустить сейчас?'");
			
			ПоказатьВопрос(Оповещение, ТекстВопроса, РежимДиалогаВопрос.ДаНет);
			
		Иначе 
			
			Оповещение = Контекст.Оповещение;
			
			Зарегистрировано = Истина;
			ВыполнитьОбработкуОповещения(Оповещение, Зарегистрировано);
			
		КонецЕсли;
		
	Иначе 
		
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = 'Не удалось зарегистрировать компоненту comcntr.
			           |Код ошибки regsvr32: %1'"),
			КодВозврата);
			
		Если КодВозврата = 1 Тогда
			ТекстСообщения = ТекстСообщения + " " + НСтр("ru = 'Ошибка разбора командной строки.'");
		ИначеЕсли КодВозврата = 2 Тогда
			ТекстСообщения = ТекстСообщения + " " + НСтр("ru = 'Ошибка инициализации COM библиотеки.'");
		ИначеЕсли КодВозврата = 3 Тогда
			ТекстСообщения = ТекстСообщения + " " + НСтр("ru = 'Ошибка загрузки модуля из COM библиотеки.'");
		ИначеЕсли КодВозврата = 4 Тогда
			ТекстСообщения = ТекстСообщения + " " + НСтр("ru = 'Ошибка получения адреса функции или переменной из COM-библиотеки.'");
		ИначеЕсли КодВозврата = 5 Тогда
			ТекстСообщения = ТекстСообщения + " " + НСтр("ru = 'Ошибка исполнения функции регистрации.'");
		Иначе 
			ТекстСообщения = ТекстСообщения + Символы.ПС + ОписаниеОшибки;
		КонецЕсли;
		
		ЖурналРегистрацииКлиент.ДобавитьСообщениеДляЖурналаРегистрации(
			НСтр("ru = 'Регистрация компоненты comcntr'", ОбщегоНазначенияКлиент.КодОсновногоЯзыка()),
			"Ошибка",
			ТекстСообщения,,
			Истина);
		
		Оповещение = Новый ОписаниеОповещения("ЗарегистрироватьCOMСоединительОповеститьОбОшибке", 
			ОбщегоНазначенияСлужебныйКлиент, Контекст);
		
		ПоказатьПредупреждение(Оповещение, ТекстСообщения);
		
	КонецЕсли;
	
КонецПроцедуры

// Продолжение процедуры ОбщегоНазначенияКлиент.ЗарегистрироватьCOMСоединитель.
Процедура ЗарегистрироватьCOMСоединительПриПроверкеОтветаОПерезапускеСеанса(Ответ, Контекст) Экспорт
	
	Если Ответ = КодВозвратаДиалога.Да Тогда
		ПараметрыПриложения.Вставить("СтандартныеПодсистемы.ПропуститьПредупреждениеПередЗавершениемРаботыСистемы", Истина);
		ЗавершитьРаботуСистемы(Истина, Истина);
	Иначе 
		ЗарегистрироватьCOMСоединительОповеститьОбОшибке(Контекст);
	КонецЕсли;

КонецПроцедуры

// Продолжение процедуры ОбщегоНазначенияКлиент.ЗарегистрироватьCOMСоединитель.
Процедура ЗарегистрироватьCOMСоединительОповеститьОбОшибке(Контекст) Экспорт
	
	Оповещение = Контекст.Оповещение;
	
	Если Оповещение <> Неопределено Тогда
		Зарегистрировано = Ложь;
		ВыполнитьОбработкуОповещения(Оповещение, Зарегистрировано);
	КонецЕсли;
	
КонецПроцедуры

// Продолжение процедуры ОбщегоНазначенияКлиент.ЗарегистрироватьCOMСоединитель.
//
// Возвращаемое значение:
//   Булево
//
Функция ЗарегистрироватьCOMСоединительДоступнаРегистрация() Экспорт
	
#Если ВебКлиент Или МобильныйКлиент Тогда
	Возврат Ложь;
#Иначе
	Возврат Не ОбщегоНазначенияКлиент.КлиентПодключенЧерезВебСервер()
	      И Не СтандартныеПодсистемыКлиент.ЭтоБазоваяВерсияКонфигурации()
	      И Не СтандартныеПодсистемыКлиент.ЭтоУчебнаяПлатформа();
#КонецЕсли
	
КонецФункции

#КонецОбласти

#Область ТабличныйДокумент

Процедура ПродолжитьРасчетПоказателей(Результат, ДополнительныеПараметры) Экспорт 
	
	Если Результат = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Если Результат.Статус = "Ошибка" Тогда
		ВызватьИсключение Результат.КраткоеПредставлениеОшибки;
	КонецЕсли;
	
	Если Результат.Статус <> "Выполнено" Тогда
		Возврат;
	КонецЕсли;
		
	РасчетныеПоказатели = ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
	
	ЗавершитьРасчетПоказателей(
		ДополнительныеПараметры.Форма,
		ДополнительныеПараметры.ТекущаяКоманда,
		ДополнительныеПараметры.МинимальноеКоличество,
		РасчетныеПоказатели);
	
КонецПроцедуры

Процедура ЗавершитьРасчетПоказателей(Форма, ТекущаяКоманда, МинимальноеКоличество, РасчетныеПоказатели) 
	
	Элементы = Форма.Элементы;
	
	ЗаполнитьЗначенияСвойств(Форма, РасчетныеПоказатели);
	
	КомандыПоказателей = КомандыПоказателей();
	
	Для Каждого Команда Из КомандыПоказателей Цикл 
		ИзменитьСвойствоЭлементаРасчетаПоказателей(Элементы, Команда.Ключ, "Пометка", Ложь);
		
		ЗначениеПоказателя = РасчетныеПоказатели[Команда.Значение];
		Элементы[Команда.Значение].ФорматРедактирования = ФорматРедактированияПоказателя(ЗначениеПоказателя);
	КонецЦикла;
	
	ИзменитьСвойствоЭлементаРасчетаПоказателей(Элементы, ТекущаяКоманда, "Пометка", Истина);
	
	ТекущийПоказатель = КомандыПоказателей[ТекущаяКоманда];
	
	Если ТекущийПоказатель = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Если РасчетныеПоказатели.Количество >= МинимальноеКоличество  Тогда 
		Форма.Показатель = Форма[ТекущийПоказатель];
		Элементы.Показатель.ФорматРедактирования = Элементы[ТекущийПоказатель].ФорматРедактирования;
	КонецЕсли;
	
	ИзменитьСвойствоЭлементаРасчетаПоказателей(
		Элементы, "КомандыВидовПоказателей", "Картинка", БиблиотекаКартинок[ТекущийПоказатель]);
	
	ИзменитьСвойствоЭлементаРасчетаПоказателей(
		Элементы, "ВыбратьПоказатель", "Картинка", БиблиотекаКартинок[ТекущийПоказатель]);
	
	Форма.ОсновнойПоказатель = ТекущаяКоманда;
	Форма.РазвернутьОбластьПоказателей = Элементы.РассчитатьВсеПоказатели.Пометка;
	
КонецПроцедуры

Функция ТекущаяКомандаРасчетаПоказателей(ЭлементыФормы)
	
	Перем ТекущаяКоманда;
	
	КомандыПоказателей = КомандыПоказателей();
	Для Каждого Команда Из КомандыПоказателей Цикл 
		
		Если ЭлементыФормы[Команда.Ключ].Пометка Тогда 
			
			ТекущаяКоманда = Команда.Ключ;
			Прервать;
			
		КонецЕсли;
		
	КонецЦикла;
	
	Если ТекущаяКоманда = Неопределено Тогда 
		ТекущаяКоманда = "РассчитатьСумму";
	КонецЕсли;
	
	Возврат ТекущаяКоманда;
	
КонецФункции

// Определяет соответствие между командами расчета показателей и показателями.
//
// Возвращаемое значение:
//   Соответствие из КлючИЗначение:
//     * Ключ - Строка - имя команды;
//     * Значение - Строка - имя показателя.
//
Функция КомандыПоказателей()
	
	КомандыПоказателей = Новый Соответствие();
	КомандыПоказателей.Вставить("РассчитатьСумму", "Сумма");
	КомандыПоказателей.Вставить("РассчитатьКоличество", "Количество");
	КомандыПоказателей.Вставить("РассчитатьСреднее", "Среднее");
	КомандыПоказателей.Вставить("РассчитатьМинимум", "Минимум");
	КомандыПоказателей.Вставить("РассчитатьМаксимум", "Максимум");
	
	Возврат КомандыПоказателей;
	
КонецФункции

Функция ФорматРедактированияПоказателя(ЗначениеПоказателя)
	
	ШаблонФорматаРедактирования = "ЧДЦ=%1; ЧРГ=' '; ЧН=0";
	
	ЗначениеДробнойЧасти = Макс(ЗначениеПоказателя, -ЗначениеПоказателя) % 1;
	РазрядностьДробнойЧасти = Мин(?(ЗначениеДробнойЧасти = 0, 0, СтрДлина(ЗначениеДробнойЧасти) - 2), 5);
	
	ФорматРедактирования = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		ШаблонФорматаРедактирования, РазрядностьДробнойЧасти);
	
	ПредставлениеПоказателя = Формат(ЗначениеПоказателя, ФорматРедактирования);
	
	Пока РазрядностьДробнойЧасти > 0
		И СтрЗаканчиваетсяНа(ПредставлениеПоказателя, "0") Цикл 
		
		ПредставлениеПоказателя = Сред(ПредставлениеПоказателя, 1, СтрДлина(ПредставлениеПоказателя) - 1);
		РазрядностьДробнойЧасти = РазрядностьДробнойЧасти - 1;
		
	КонецЦикла;
	
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		ШаблонФорматаРедактирования, РазрядностьДробнойЧасти);
	
КонецФункции

Процедура ИзменитьСвойствоЭлементаРасчетаПоказателей(ЭлементыФормы, ИмяЭлемента, ИмяСвойства, ЗначениеСвойства)
	
	СписокИменЭлементов = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку("%1, %1%2", ИмяЭлемента, "Еще");
	ИменаЭлементов = СтрРазделить(СписокИменЭлементов, ", ", Ложь);
	
	Для Каждого Имя Из ИменаЭлементов Цикл 
		
		НайденныйЭлемент = ЭлементыФормы.Найти(Имя);
		
		Если НайденныйЭлемент <> Неопределено Тогда 
			НайденныйЭлемент[ИмяСвойства] = ЗначениеСвойства;
		КонецЕсли;
		
	КонецЦикла;
	
КонецПроцедуры

#КонецОбласти

#Область ХешСтрокиПоАлгоритмуMD5

// АПК:1353-выкл, АПК:247-выкл Короткие имена математических операндов. 

// Возвращает хеш по алгоритму MD5 для произвольной строки.
//
// Параметры:
//  Строка - Строка - произвольная строка любой длины
//
// Возвращаемое значение:
//  Строка - хеш, вычисленный из строки
////
// Возвращает хеш по алгоритму MD5 для произвольной строки.
//
// Параметры:
//  Строка - строка - произвольная строка любой длины
//
// Возвращаемое значение:
//  Строка - хеш, вычисленный из строки
//
Функция ВычислитьХешСтрокиПоАлгоритмуMD5(Знач Строка)
	
	a = 1732584193; // 01 23 45 67; (шестнадцатеричное представление, сначала младший байт)
	b = 4023233417; // 89 AB CD EF;
	c = 2562383102; // FE DC BA 98;
	d = 271733878;  // 76 54 32 10;
	
	X = Новый Массив(16); // X - блок данных размером 512 бит, массив из 32-битных слов
	
	ПустойБайт = ПолучитьДвоичныеДанныеИзHexСтроки("00");
	БайтОкончания = ПолучитьДвоичныеДанныеИзHexСтроки("80");
	ДвоичныеДанные = ДобавитьКДвоичнымДанным(ПолучитьДвоичныеДанныеИзСтроки(Строка, "UTF-8"), БайтОкончания);
	
	// разбиваем строку на блоки по 512 бит
	МассивБлоковИзСтроки = РазделитьДвоичныеДанные(ДвоичныеДанные, 64);
	
	ПоследнийБлок = МассивБлоковИзСтроки[МассивБлоковИзСтроки.ВГраница()];
	Пока ПоследнийБлок.Размер() < 64 Цикл
		ПоследнийБлок = ДобавитьКДвоичнымДанным(ПоследнийБлок, ПустойБайт);
	КонецЦикла;
	
	МассивБлоковИзСтроки[МассивБлоковИзСтроки.ВГраница()] = ПоследнийБлок;
	
	БуферДанных = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(ПоследнийБлок);
	Если БуферДанных.ПрочитатьЦелое64(56) <> 0 Тогда
		МассивБлоковИзСтроки.Добавить(0);
	КонецЕсли;

	// для каждого блока производим расчет
	Для НомерБлока = 0 По МассивБлоковИзСтроки.Количество() - 1 Цикл 
		Блок = МассивБлоковИзСтроки[НомерБлока];
		
		Если Блок = 0 Тогда
			X = Новый Массив(16);
			Для НомерСлова = 0 По 15 Цикл
				X[НомерСлова] = 0;
			КонецЦикла;
		Иначе
			БуферДанных = ПолучитьБуферДвоичныхДанныхИзДвоичныхДанных(Блок);
			Для НомерСлова = 0 По 15 Цикл
				Слово = БуферДанных.ПрочитатьЦелое32(НомерСлова*4);
				X[НомерСлова] = ?(Слово = Неопределено, 0, Слово);
			КонецЦикла;
		КонецЕсли;
 
		// в последний блок дописываем длину строки в битах
		Если НомерБлока = МассивБлоковИзСтроки.Количество() - 1 Тогда
			РазмерСтрокиВБитах = ПолучитьДвоичныеДанныеИзСтроки(Строка).Размер()* 8;
			X[14] = РазмерСтрокиВБитах % Pow(2,32); // сначала младшие 4 байта
			X[15] = Цел(РазмерСтрокиВБитах / Pow(2,32)) % Pow(2,64); // если длина превосходит (2^64 - 1) бит, то старшие биты откидываем
		КонецЕсли;
		РассчитатьБлок(a, b, c, d, X);
	КонецЦикла;
	                                                   
	Результат = ПолучитьHexСтрокуИзДвоичныхДанных(ЧислоВДвоичныеДанные(a))
			  + ПолучитьHexСтрокуИзДвоичныхДанных(ЧислоВДвоичныеДанные(b))
			  + ПолучитьHexСтрокуИзДвоичныхДанных(ЧислоВДвоичныеДанные(c))
			  + ПолучитьHexСтрокуИзДвоичныхДанных(ЧислоВДвоичныеДанные(d));
	
	Возврат Результат;
	
КонецФункции

Функция СократитьСтроку(Строка, ИспользуемоеКоличествоСимволов)
	 Результат = Лев(Строка, ИспользуемоеКоличествоСимволов);
	 Результат = Результат + ВычислитьХешСтрокиПоАлгоритмуMD5(Сред(Строка, ИспользуемоеКоличествоСимволов+1));
	 Возврат Результат;
КонецФункции
 
Функция РазмерСтрокиВБайтах(Знач Строка)
	
	Возврат ПолучитьДвоичныеДанныеИзСтроки(Строка, "UTF-8").Размер();

КонецФункции

Процедура РассчитатьБлок(a, b, c, d, X)
	aa = a;
	bb = b;
	cc = c;
	dd = d;
	
	// раунд 1
	ВыполнитьОперациюСФункциейF(a,b,c,d, X[ 0],  7, 3614090360); // 0xd76aa478 /* 1 */
	ВыполнитьОперациюСФункциейF(d,a,b,c, X[ 1], 12, 3905402710); // 0xe8c7b756 /* 2 */
	ВыполнитьОперациюСФункциейF(c,d,a,b, X[ 2], 17,  606105819); // 0x242070db /* 3 */
	ВыполнитьОперациюСФункциейF(b,c,d,a, X[ 3], 22, 3250441966); // 0xc1bdceee /* 4 */
	ВыполнитьОперациюСФункциейF(a,b,c,d, X[ 4],  7, 4118548399); // 0xf57c0faf /* 5 */
	ВыполнитьОперациюСФункциейF(d,a,b,c, X[ 5], 12, 1200080426); // 0x4787c62a /* 6 */
	ВыполнитьОперациюСФункциейF(c,d,a,b, X[ 6], 17, 2821735955); // 0xa8304613 /* 7 */
	ВыполнитьОперациюСФункциейF(b,c,d,a, X[ 7], 22, 4249261313); // 0xfd469501 /* 8 */
	ВыполнитьОперациюСФункциейF(a,b,c,d, X[ 8],  7, 1770035416); // 0x698098d8 /* 9 */
	ВыполнитьОперациюСФункциейF(d,a,b,c, X[ 9], 12, 2336552879); // 0x8b44f7af /* 10 */
	ВыполнитьОперациюСФункциейF(c,d,a,b, X[10], 17, 4294925233); // 0xffff5bb1 /* 11 */
	ВыполнитьОперациюСФункциейF(b,c,d,a, X[11], 22, 2304563134); // 0x895cd7be /* 12 */
	ВыполнитьОперациюСФункциейF(a,b,c,d, X[12],  7, 1804603682); // 0x6b901122 /* 13 */
	ВыполнитьОперациюСФункциейF(d,a,b,c, X[13], 12, 4254626195); // 0xfd987193 /* 14 */
	ВыполнитьОперациюСФункциейF(c,d,a,b, X[14], 17, 2792965006); // 0xa679438e /* 15 */
	ВыполнитьОперациюСФункциейF(b,c,d,a, X[15], 22, 1236535329); // 0x49b40821 /* 16 */
	
	// раунд 2
	ВыполнитьОперациюСФункциейG(a,b,c,d, X[ 1],  5, 4129170786); // 0xf61e2562 /* 17 */
	ВыполнитьОперациюСФункциейG(d,a,b,c, X[ 6],  9, 3225465664); // 0xc040b340 /* 18 */
	ВыполнитьОперациюСФункциейG(c,d,a,b, X[11], 14,  643717713); // 0x265e5a51 /* 19 */
	ВыполнитьОперациюСФункциейG(b,c,d,a, X[ 0], 20, 3921069994); // 0xe9b6c7aa /* 20 */
	ВыполнитьОперациюСФункциейG(a,b,c,d, X[ 5],  5, 3593408605); // 0xd62f105d /* 21 */
	ВыполнитьОперациюСФункциейG(d,a,b,c, X[10],  9,   38016083); //  0x2441453 /* 22 */
	ВыполнитьОперациюСФункциейG(c,d,a,b, X[15], 14, 3634488961); // 0xd8a1e681 /* 23 */
	ВыполнитьОперациюСФункциейG(b,c,d,a, X[ 4], 20, 3889429448); // 0xe7d3fbc8 /* 24 */
	ВыполнитьОперациюСФункциейG(a,b,c,d, X[ 9],  5,  568446438); // 0x21e1cde6 /* 25 */
	ВыполнитьОперациюСФункциейG(d,a,b,c, X[14],  9, 3275163606); // 0xc33707d6 /* 26 */
	ВыполнитьОперациюСФункциейG(c,d,a,b, X[ 3], 14, 4107603335); // 0xf4d50d87 /* 27 */
	ВыполнитьОперациюСФункциейG(b,c,d,a, X[ 8], 20, 1163531501); // 0x455a14ed /* 28 */
	ВыполнитьОперациюСФункциейG(a,b,c,d, X[13],  5, 2850285829); // 0xa9e3e905 /* 29 */
	ВыполнитьОперациюСФункциейG(d,a,b,c, X[ 2],  9, 4243563512); // 0xfcefa3f8 /* 30 */
	ВыполнитьОперациюСФункциейG(c,d,a,b, X[ 7], 14, 1735328473); // 0x676f02d9 /* 31 */
	ВыполнитьОперациюСФункциейG(b,c,d,a, X[12], 20, 2368359562); // 0x8d2a4c8a /* 32 */
	
	// раунд 3
	ВыполнитьОперациюСФункциейH(a,b,c,d, X[ 5],  4, 4294588738); // 0xfffa3942 /* 33 */
	ВыполнитьОперациюСФункциейH(d,a,b,c, X[ 8], 11, 2272392833); // 0x8771f681 /* 34 */
	ВыполнитьОперациюСФункциейH(c,d,a,b, X[11], 16, 1839030562); // 0x6d9d6122 /* 35 */
	ВыполнитьОперациюСФункциейH(b,c,d,a, X[14], 23, 4259657740); // 0xfde5380c /* 36 */
	ВыполнитьОперациюСФункциейH(a,b,c,d, X[ 1],  4, 2763975236); // 0xa4beea44 /* 37 */
	ВыполнитьОперациюСФункциейH(d,a,b,c, X[ 4], 11, 1272893353); // 0x4bdecfa9 /* 38 */
	ВыполнитьОперациюСФункциейH(c,d,a,b, X[ 7], 16, 4139469664); // 0xf6bb4b60 /* 39 */
	ВыполнитьОперациюСФункциейH(b,c,d,a, X[10], 23, 3200236656); // 0xbebfbc70 /* 40 */
	ВыполнитьОперациюСФункциейH(a,b,c,d, X[13],  4,  681279174); // 0x289b7ec6 /* 41 */
	ВыполнитьОперациюСФункциейH(d,a,b,c, X[ 0], 11, 3936430074); // 0xeaa127fa /* 42 */
	ВыполнитьОперациюСФункциейH(c,d,a,b, X[ 3], 16, 3572445317); // 0xd4ef3085 /* 43 */
	ВыполнитьОперациюСФункциейH(b,c,d,a, X[ 6], 23,   76029189); //  0x4881d05 /* 44 */
	ВыполнитьОперациюСФункциейH(a,b,c,d, X[ 9],  4, 3654602809); // 0xd9d4d039 /* 45 */
	ВыполнитьОперациюСФункциейH(d,a,b,c, X[12], 11, 3873151461); // 0xe6db99e5 /* 46 */
	ВыполнитьОперациюСФункциейH(c,d,a,b, X[15], 16,  530742520); // 0x1fa27cf8 /* 47 */
	ВыполнитьОперациюСФункциейH(b,c,d,a, X[ 2], 23, 3299628645); // 0xc4ac5665 /* 48 */
	
	// раунд 4
	ВыполнитьОперациюСФункциейI(a,b,c,d, X[ 0],  6, 4096336452); // 0xf4292244 /* 49 */
	ВыполнитьОперациюСФункциейI(d,a,b,c, X[ 7], 10, 1126891415); // 0x432aff97 /* 50 */
	ВыполнитьОперациюСФункциейI(c,d,a,b, X[14], 15, 2878612391); // 0xab9423a7 /* 51 */
	ВыполнитьОперациюСФункциейI(b,c,d,a, X[ 5], 21, 4237533241); // 0xfc93a039 /* 52 */
	ВыполнитьОперациюСФункциейI(a,b,c,d, X[12],  6, 1700485571); // 0x655b59c3 /* 53 */
	ВыполнитьОперациюСФункциейI(d,a,b,c, X[ 3], 10, 2399980690); // 0x8f0ccc92 /* 54 */
	ВыполнитьОперациюСФункциейI(c,d,a,b, X[10], 15, 4293915773); // 0xffeff47d /* 55 */
	ВыполнитьОперациюСФункциейI(b,c,d,a, X[ 1], 21, 2240044497); // 0x85845dd1 /* 56 */
	ВыполнитьОперациюСФункциейI(a,b,c,d, X[ 8],  6, 1873313359); // 0x6fa87e4f /* 57 */
	ВыполнитьОперациюСФункциейI(d,a,b,c, X[15], 10, 4264355552); // 0xfe2ce6e0 /* 58 */
	ВыполнитьОперациюСФункциейI(c,d,a,b, X[ 6], 15, 2734768916); // 0xa3014314 /* 59 */
	ВыполнитьОперациюСФункциейI(b,c,d,a, X[13], 21, 1309151649); // 0x4e0811a1 /* 60 */
	ВыполнитьОперациюСФункциейI(a,b,c,d, X[ 4],  6, 4149444226); // 0xf7537e82 /* 61 */
	ВыполнитьОперациюСФункциейI(d,a,b,c, X[11], 10, 3174756917); // 0xbd3af235 /* 62 */
	ВыполнитьОперациюСФункциейI(c,d,a,b, X[ 2], 15,  718787259); // 0x2ad7d2bb /* 63 */
	ВыполнитьОперациюСФункциейI(b,c,d,a, X[ 9], 21, 3951481745); // 0xeb86d391 /* 64 */
	
	a = БинарнаяСумма(a, aa);
	b = БинарнаяСумма(b, bb);
	c = БинарнаяСумма(c, cc);
	d = БинарнаяСумма(d, dd);
КонецПроцедуры

Процедура ВыполнитьОперациюСФункциейF(a, b, c, d, X, s, t)
	ВыполнитьОперацию(ПобитовоеИли(ПобитовоеИ(b, c), ПобитовоеИ(ПобитовоеНе(b), d)), a, b, X, s, t);
КонецПроцедуры

Процедура ВыполнитьОперациюСФункциейG(a, b, c, d, X, s, t)
	ВыполнитьОперацию(ПобитовоеИли(ПобитовоеИ(b, d), ПобитовоеИ(ПобитовоеНе(d), c)), a, b, X, s, t);
КонецПроцедуры

Процедура ВыполнитьОперациюСФункциейH(a, b, c, d, X, s, t)
	ВыполнитьОперацию(ПобитовоеИсключительноеИли(ПобитовоеИсключительноеИли(b, c), d), a, b, X, s, t);
КонецПроцедуры

Процедура ВыполнитьОперациюСФункциейI(a, b, c, d, X, s, t)
	ВыполнитьОперацию(ПобитовоеИсключительноеИли(ПобитовоеИли(ПобитовоеНе(d), b), c), a, b, X, s, t);
КонецПроцедуры

Процедура ВыполнитьОперацию(q, a, b, X, s, t)
	a = БинарнаяСумма(ЦиклическийСдвигВлево(БинарнаяСумма(БинарнаяСумма(a, q), БинарнаяСумма(X, t)), s), b);
КонецПроцедуры

Функция ЦиклическийСдвигВлево(Число, КоличествоРазрядов)
	Результат = Число;
	Для НомерРазряда = 1 По КоличествоРазрядов Цикл
		Бит = ПроверитьБит(Результат, 31);
		Результат = ПобитовыйСдвигВлево(Результат, 1)% Pow(2,32);
		Результат = УстановитьБит(Результат, 0, Бит);
	КонецЦикла;
	Возврат Результат;
КонецФункции

Функция ЧислоВДвоичныеДанные(Число)
	
	БуферДанных = Новый БуферДвоичныхДанных(4);
	БуферДанных.ЗаписатьЦелое32(0, Число);
	ДвоичныеДанные = ПолучитьДвоичныеДанныеИзБуфераДвоичныхДанных(БуферДанных);
	Возврат ДвоичныеДанные;
	
КонецФункции

Функция БинарнаяСумма(Аргумент1, Аргумент2)
	Результат = (Аргумент1+Аргумент2)% Pow(2,32);
	Возврат Результат;
КонецФункции

Функция ДобавитьКДвоичнымДанным(ДвоичныеДанные, Добавление)
	МассивДвоичныхДанных = Новый Массив;
	МассивДвоичныхДанных.Добавить(ДвоичныеДанные);
	МассивДвоичныхДанных.Добавить(Добавление);
	Возврат СоединитьДвоичныеДанные(МассивДвоичныхДанных);
КонецФункции

// АПК:1353-вкл, АПК:247-вкл 

#КонецОбласти

#Область УстаревшиеПроцедурыИФункции

// Устарела. Используется в ОбщегоНазначенияКлиент.ПроверитьРасширениеРаботыСФайламиПодключено.
Процедура ПроверитьРасширениеРаботыСФайламиПодключеноЗавершение(РасширениеПодключено, ДополнительныеПараметры) Экспорт
	
	Если РасширениеПодключено Тогда
		ВыполнитьОбработкуОповещения(ДополнительныеПараметры.ОписаниеОповещенияОЗакрытии);
		Возврат;
	КонецЕсли;
	
	ТекстСообщения = ДополнительныеПараметры.ТекстПредупреждения;
	Если ПустаяСтрока(ТекстСообщения) Тогда
		ТекстСообщения = НСтр("ru = 'Действие недоступно, так как не установлено расширение для работы с 1С:Предприятием.'")
	КонецЕсли;
	ПоказатьПредупреждение(, ТекстСообщения);
	
КонецПроцедуры

#КонецОбласти

#КонецОбласти